#pragma once

#include <array>
#include <cstring>
#include <project_config.h>
#include <spi.hpp>
#include <stm32f4xx_hal.h>


namespace bsp::m25p16
{
    /* Flash指令定义 */
    enum class cmd: uint8_t
    {
        WriteEnable      = 0x06,
        ReadStatusReg    = 0x05,
        WriteStatusReg   = 0x01,
        ReadData         = 0x03,
        PageProgram      = 0x02,
        BlockErase       = 0xD8,
        SectorErase      = 0x20,
        ChipErase        = 0xC7,
        W25X_JedecDeviceID=0x9F,
    };

    /* 常量定义 */
    enum class config: uint16_t {
        Dummy_Byte       = 0xFF,
        WIP_Flag         = 0x01,
        PAGE_SIZE       = 256,
   };


    class control
    {
        using spi  = hal::spi3;
        using cs  = hal::gpio::pinOld<GPIOC_BASE,GPIO_PIN_9>;
    public:
        static void init()
        {
            spi::init();
            SPI_FLASH_WriteEnable();
            cs::clear();
            SPI_FLASH_TransferByte(0x50);// 写状态寄存器使能
            cs::set();
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::WriteStatusReg));
            SPI_FLASH_TransferByte(0x00);// 清除状态寄存器
            cs::set();
            SPI_FLASH_WaitForWriteEnd();
        }
        //已验证
        static void sector_erase(const uint32_t SectorAddr) noexcept {
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::SectorErase));
            SPI_FLASH_TransferAddress(SectorAddr);
            cs::set();
            SPI_FLASH_WaitForWriteEnd();
        }
        template <uint8_t FAST_READ_WRITE>
        static void page_write(uint8_t* pBuffer, const uint32_t WriteAddr, const uint16_t NumByteToWrite) noexcept {
            //这句是一定要加的
            SPI_FLASH_WriteEnable();
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::PageProgram));
            SPI_FLASH_TransferAddress(WriteAddr);
            SPI_FLASH_TransmitData<FAST_READ_WRITE>(pBuffer, NumByteToWrite);
            cs::set();
            SPI_FLASH_WaitForWriteEnd();
            // cs::set();
        }

        /* 异步操作实现 未验证 */
        static void async_chip_erase() noexcept
        {
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::ChipErase));
            cs::set();
            // 注意：异步操作不等待完成
        }

        static void chip_erase()
        {
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::ChipErase));
            cs::set();
            SPI_FLASH_WaitForWriteEnd();
            cs::set();
        }

        static uint8_t async_is_busy() noexcept {
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::ReadStatusReg));
            const uint8_t temp= SPI_FLASH_TransferByte(static_cast<uint8_t>(config::Dummy_Byte));
            cs::set();
            return temp & static_cast<uint8_t>(config::WIP_Flag);
        }

        /**
        * @brief  读取FLASH数据
        * @param pBuffer 存储读出数据的指针
        * @param ReadAddr 读取地址
        * @param NumByteToRead 读取数据长度
        * @retval 无
        */
        static void buffer_read(uint8_t *pBuffer, const uint32_t ReadAddr, uint16_t NumByteToRead)
        {
            /* 选择FLASH: CS低电平 */
            cs::clear();
            /* 发送 读 指令 */
            spi::send_byte(static_cast<uint8_t>(cmd::ReadData));
            /* 传输地址 */
            SPI_FLASH_TransferAddress(ReadAddr);

            uint8_t dummy[NumByteToRead]={};

            memset(dummy, static_cast<int>(config::Dummy_Byte), NumByteToRead);
            /* 读取数据 */
            spi::send_receive_bytes(dummy,pBuffer,NumByteToRead);
            /* 停止信号 FLASH: CS 高电平 */
            cs::set();
        }
        static uint32_t read_id()
        {
            /* 开始通讯：CS低电平 */
            cs::clear();

            /* 发送JEDEC指令，读取ID */
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::W25X_JedecDeviceID));
            uint8_t temp_1= SPI_FLASH_TransferByte(static_cast<uint8_t>(config::Dummy_Byte));
            uint8_t temp_2= SPI_FLASH_TransferByte(static_cast<uint8_t>(config::Dummy_Byte));
            uint8_t temp_3= SPI_FLASH_TransferByte(static_cast<uint8_t>(config::Dummy_Byte));
            cs::set();
            uint32_t temp = (temp_1 << 16) | (temp_2 << 8) | temp_3;

            return temp;

        }
    private:
        // 写使能命令
        static void SPI_FLASH_WriteEnable() noexcept {
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::WriteEnable));
            cs::set();
        }
        // SPI单字节传输
        static uint8_t SPI_FLASH_TransferByte(const uint8_t byte) noexcept {
            uint8_t temp = 0;
            spi::send_receive_byte(byte, temp);
            return temp;
        }
        // 等待写操作完成
        static void SPI_FLASH_WaitForWriteEnd() noexcept {
            uint8_t status = 0;
            cs::clear();
            SPI_FLASH_TransferByte(static_cast<uint8_t>(cmd::ReadStatusReg));
            do {
                status = SPI_FLASH_TransferByte(static_cast<uint8_t>(config::Dummy_Byte));
            } while (status & static_cast<uint8_t>(config::WIP_Flag));
            cs::set();
        }
        // 数据传输封装
        template <uint8_t FAST_READ_WRITE>
        static void SPI_FLASH_TransmitData(uint8_t* pBuffer, const uint16_t size) noexcept {
            // 送数据时不需要send_receive_byte
            if constexpr (FAST_READ_WRITE)
            {
                spi::send_bytes(pBuffer,size);
            }

            else
            {
                uint8_t temp;
                for(uint16_t i=0; i<size; ++i) {
                    spi::send_receive_byte(pBuffer[i],temp);
                }
            }

        }

        template <uint8_t FAST_READ_WRITE>
        static void SPI_FLASH_ReceiveData(uint8_t* pBuffer, const uint16_t size) noexcept {
            if constexpr (FAST_READ_WRITE)
                spi::receive_bytes(pBuffer, size);
            else
                for(uint16_t i=0; i<size; ++i) {
                    pBuffer[i] = SPI_FLASH_TransferByte(static_cast<uint8_t>(config::Dummy_Byte));
                }
        }

        // 地址传输（24bit）
        static void SPI_FLASH_TransferAddress(const uint32_t Address) noexcept {
            SPI_FLASH_TransferByte((Address >> 16) & 0xFF);
            SPI_FLASH_TransferByte((Address >> 8) & 0xFF);
            SPI_FLASH_TransferByte(Address & 0xFF);
        }
    };
}